
#include "area.h"
#include "exit.h"
#include "room.h"
#include "terrainTable.h"
#include "world.h"

Area::Area( void ) {
  return;
}

Area::Area( const unsigned long& low, const unsigned long& high ) {
  name( "Undefined" );
  this->low( low );
  this->high( high );
  terrain( TerrainTable::Instance().find( "city" ) );
  create();
}

Area::~Area( void ) {
  return;
}

void Area::create( void ) { // private
  try {
    char query[MAX_BUFFER];

    sprintf( query,
      "INSERT INTO areas    \
        ( low, high, name ) \
        VALUES              \
        ( %lu, %lu, '%s' ); \
      ;",
      low(),
      high(),
      Mysql::addslashes(name()).c_str()
    );
    World::Instance().getMysql()->insert( query );
    ID( World::Instance().getMysql()->getInsertID() );

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to create area: %s\n", me.getMessage().c_str() );
    return;
  }

  return;
}

bool Area::load( ROW& row ) { // public
  ID( row["areaID"] );
  low( row["low"] );
  high( row["high"] );
  name( row["name"] );
  terrain( TerrainTable::Instance().find( row["terrain"] ) );
  return true;
}

bool Area::loadRooms( void ) { // public
  try {
    Mysql* mysql = World::Instance().getMysql();
    ROW row;
    char query[MAX_BUFFER];

    sprintf( query, "SELECT * FROM rooms WHERE areaID = %lu ORDER BY vnum ASC;", ID() );
    if ( mysql->select( query ) ) {
      while ( row = mysql->fetch() ) {
        rooms().insert( std::make_pair( row["vnum"], new Room( this, row ) ) );
      }
    }

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to load rooms for area %lu: %s\n", ID(), me.getMessage().c_str() );
    return false;
  }

  return true;
}

bool Area::loadExits( void ) { // public
  try {
    Mysql* mysql = World::Instance().getMysql();
    Room* target = NULL;
    ROW row;
    char query[MAX_BUFFER];
    for ( std::map<unsigned long,Room*>::iterator it = rooms().begin(); it != rooms().end(); ++it ) {
      sprintf( query, "SELECT * FROM exits WHERE vnum = %lu LIMIT 6;", it->second->vnum() );
      if ( mysql->select( query ) ) {
        while ( row = mysql->fetch() ) {
          if ( ( target = World::Instance().findRoom( row["target"] ) ) != NULL ) {
            it->second->exit( row["direction"], new Exit( target, row ) );
          }
        }
      }
    }
  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to load exits for area %lu: %s\n", ID(), me.getMessage().c_str() );
    return false;
  }

  return true;
}

bool Area::loadObjects( void ) { // public
  try {
    Mysql* mysql = World::Instance().getMysql();
    ROW row;
    char query[MAX_BUFFER];

    sprintf( query, "SELECT * FROM objects WHERE areaID = %lu ORDER BY vnum ASC;", ID() );
    if ( mysql->select( query ) ) {
      while ( row = mysql->fetch() ) {
        objects().insert( std::make_pair( row["vnum"], Object::create( row["type"], row ) ) );
      }
    }

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to load objects for area %lu: %s\n", ID(), me.getMessage().c_str() );
    return false;
  }

  return true;
}

bool Area::loadMobs( void ) { // public
  try {
    Mysql* mysql = World::Instance().getMysql();
    ROW row;
    char query[MAX_BUFFER];

    sprintf( query, "SELECT * FROM mobs WHERE areaID = %lu ORDER BY vnum ASC;", ID() );
    if ( mysql->select( query ) ) {
      while ( row = mysql->fetch() ) {
        mobs().insert( std::make_pair( row["vnum"], Mob::create( row ) ) );
      }
    }

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to load mobs for area %lu: %s\n", ID(), me.getMessage().c_str() );
    return false;
  }

  return true;
}

bool Area::save( void ) { // public
  try {
    char query[MAX_BUFFER];

    sprintf( query,
      "UPDATE areas SET     \
        low = %lu,          \
        high = %lu,         \
        name = '%s',        \
        terrain = '%s'      \
       WHERE areaID = %lu   \
       LIMIT 1;",
      low(),
      high(),
      Mysql::addslashes(name()).c_str(),
      Mysql::addslashes(terrain()->name()).c_str(),
      ID()
    );
    World::Instance().getMysql()->update( query );

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to save area %lu: %s\n", ID(), me.getMessage().c_str() );
    return false;
  }

  // Save rooms...
  for ( std::map<unsigned long,Room*>::iterator it = rooms().begin(); it != rooms().end(); ++it ) {
    it->second->save();
  }

  // Save objects...
  for ( std::map<unsigned long,Object*>::iterator it = objects().begin(); it != objects().end(); ++it ) {
    it->second->save();
  }

  // Save mobs...
  for ( std::map<unsigned long,Mob*>::iterator it = mobs().begin(); it != mobs().end(); ++it ) {
    it->second->save();
  }

  return true;
}

bool Area::destroy( void ) { // public
  unsigned long tempID = ID();

  try {
    char query[MAX_BUFFER];

    sprintf( query,
      " DELETE              \
        FROM areas          \
        WHERE areaID = %lu  \
        LIMIT 1;",
      ID()
    );
    // Get rid of this (from the DB)...
    World::Instance().getMysql()->remove( query );
    // Get rid of this (from the world)...
    World::Instance().remove( this );
    // Get rid of rooms...
    while ( rooms().size() ) {
      if ( !rooms().begin()->second->destroy() ) {
        World::worldLog( ERROR, LOG_WORLD, "Room %lu failed to destroy itself with area %lu (%s).", rooms().begin()->second->vnum(), ID(), name().c_str() );
        return false;
      }
    }
    // Get rid of objects...
    while ( objects().size() ) {
      if ( !objects().begin()->second->destroy() ) {
        World::worldLog( ERROR, LOG_WORLD, "Object %lu failed to destroy itself with area %lu (%s).", objects().begin()->second->vnum(), ID(), name().c_str() );
        return false;
      }
    }
    // Get rid of this (from memory)...
    delete this;

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to delete room %lu: %s\n", tempID, me.getMessage().c_str() );
    return false;
  }

  // Success!
  return true;
}

